#include "pch.hpp"
#include "VerticalLayout.hpp"
#include "Widget.hpp"

using namespace Engine;
using namespace Engine::Ui;




VerticalLayout::VerticalLayout(boost::weak_ptr<Widget> attached)
	: Layout(attached)
{}
///////////////////////////////////////////////////////////////////////////////////////////////////

VerticalLayout::~VerticalLayout()
{}
///////////////////////////////////////////////////////////////////////////////////////////////////




void VerticalLayout::repositionChildren()
{
	if(m_resizing)
		return;

	m_resizing = true;
	// Reset the flag once the scope ends
	BOOST_SCOPE_EXIT((&m_resizing))
	{
		m_resizing = false;
	}
	BOOST_SCOPE_EXIT_END;



	auto that = attached();
	auto widgets = Layout::widgets();

	if(widgets.size())
	{
		intsize2 minimumSize   = calculateMinimumSize();
		intsize2 clientSize    = that->clientSize();
		intsize2 size          = math::max(clientSize, minimumSize);
		intsize2 optChildSize  = intsize2(size.width, size.height / widgets.size());
		/// This is the amount of space that can be given to all widgets
		intsize2 remainingSize = size;
		/// This is the number of greedy widgets (want all the size they can get)
		uint32     greedyWidgets = numWidgets(Resizing::Greedy);


		Placement::Map placed;
		foreach(const Widget::ptr& widget, widgets)
			placed.insert(Placement::Pair(widget, Placement::Opt()));


		// This remaining size is then distributed among all other widgets
		// (these are those that have no size yet)
		if(greedyWidgets)
		{
			// There are greedy widgets present (they take up ALL space)
			// This means that any normal widget will be placed with it's minimum size
			// and any static widget will be placed with it's current size
			foreach(const Widget::ptr& widget, widgets)
			{
				if(widget->resizing() == Resizing::Standard)
				{
					Placement::Opt& f = placed[widget];
					f = Placement::centered(widget, Placement::Minimum, Placement::OptimumCentered, 0, size.width);
					remainingSize.height -= f->reserved.height;
				}
				else if(widget->resizing() == Resizing::Minimum)
				{
					Placement::Opt& f = placed[widget];
					f = Placement::centered(widget, Placement::MinimumCentered, Placement::OptimumCentered, 0, size.width);
					remainingSize.height -= f->reserved.height;
				}
			}

			// All remaining widgets are now greedy ones
			// Thus there must be at least one remaining widget
			optChildSize.height = remainingSize.height / Placement::remainingWidgets(placed);
			foreach(const Widget::ptr& widget, widgets)
			{
				if(widget->resizing() == Resizing::Greedy)
				{
					Placement::Opt& f = placed[widget];
					f = Placement::centered(widget, Placement::OptimumCentered, Placement::OptimumCentered, remainingSize.height, size.width);
					remainingSize.height -= f->reserved.height;
				}
			}
		}
		else
		{
			// First, we insert the size of those widgets, that request more than
			// the average size (which means that they will get that space, no matter what).
			// We will also place all static widgets.
			foreach(const Widget::ptr& widget, widgets)
			{
				if(widget->minimumSize().height > optChildSize.height)
				{
					Placement::Opt& f = placed[widget];
					f = Placement::centered(widget, Placement::Minimum, Placement::OptimumCentered, 0, size.width);
					remainingSize.height -= f->reserved.height;
				}
			}

			// Distribute the remaining space under the rest of all widgets
			optChildSize.height = remainingSize.height / Placement::remainingWidgets(placed);

			foreach(const Widget::ptr& widget, widgets)
			{
				Placement::Opt& f = placed[widget];
				if(!f)
				{
					if(widget->resizing() == Resizing::Minimum)
					{
						Placement::Opt& f = placed[widget];
						f = Placement::centered(widget, Placement::MinimumCentered, Placement::MinimumCentered, optChildSize.height, size.width);
						remainingSize.height -= f->reserved.height;
					}
					else
					{
						f = Placement::centered(widget, Placement::OptimumCentered, Placement::OptimumCentered, optChildSize.height, size.width);
						remainingSize.height -= f->reserved.height;
					}
				}
			}
		}

		int2 pos;
		foreach(const Widget::ptr& widget, widgets)
		{
			Placement::Opt& f = placed[widget];

			widget->setPos(pos + f->pos);
			widget->resize(f->size);

			pos.y += f->reserved.height;
		}


		that->setClientSize(size);
	}
}
///////////////////////////////////////////////////////////////////////////////////////////////////

intsize2 VerticalLayout::calculateMinimumSize() const
{
	intsize2 minimumSize;

	foreach(const Widget::ptr& widget, widgets())
	{
		intsize2 minSize = widget->minimumSize();

		minimumSize.width  = std::max(minimumSize.width, minSize.width);
		minimumSize.height += minSize.height;
	}

	// Although those widgets do not want all the size, the parent
	// widget could have a minimumsize that restricts it from being too small
	int4 padd = attached()->padding();
	intsize2 configMinSize = attached()->configuredMinimumSize();
	configMinSize.width -= padd.x + padd.z;
	configMinSize.height -= padd.y + padd.w;

	return math::max(minimumSize, configMinSize);
}
///////////////////////////////////////////////////////////////////////////////////////////////////
